package club.jdiy.dev.controller;

import club.jdiy.admin.freemarker.func.GetDictInfo;
import club.jdiy.core.AdminContext;
import club.jdiy.core.base.JDiyCtrl;
import club.jdiy.core.base.Java8MyDateEditor;
import club.jdiy.core.base.domain.DBEntity;
import club.jdiy.core.base.domain.DictInfo;
import club.jdiy.core.base.domain.Ret;
import club.jdiy.core.sql.Rs;
import club.jdiy.core.sql.TableInfo;
import club.jdiy.core.storage.Store;
import club.jdiy.dev.meta.ColumnMeta;
import club.jdiy.dev.meta.TreeUiMeta;
import club.jdiy.dev.meta.UiMeta;
import club.jdiy.dev.service.JDiyUiService;
import club.jdiy.dev.types.ColFormatTpl;
import club.jdiy.dev.types.TreePathTpl;
import club.jdiy.dev.view.FtlInvoker;
import club.jdiy.dev.view.FtlParser;
import club.jdiy.utils.DateUtils;
import club.jdiy.utils.StringUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@SuppressWarnings("ALL")
public abstract class JDiyUiCtrl<T extends DBEntity, TService extends JDiyUiService> extends JDiyCtrl<T, TService> {

    /**
     * K/V键值转键名： K/V键值对映射 缓存封包
     * <pre>返回的map数据结构：
     * K = 列配置(ColumnMeta)的id.  用于获取目标列的 键值对映射
     * V =  { //目标列的 键值对映射转DictInfo
     *       _k =  键值
     *       _v = DictInfo :  name: 名称   value= 值（=_k）  color = 显示颜色（如果有）
     * }
     * </pre>
     *
     * @param columns
     * @return
     */
    protected void kvCachedWrap(Map<String, Map<String, DictInfo>> kvMaps, ColumnMeta[] columns) {
        Arrays.stream(columns).filter(it -> it.getFormat() == ColFormatTpl.kv).forEach(it -> {
            kvMaps.put(it.getId(), kv2DictInfoMap(it.getKv()));
        });
    }

    protected Map<String, DictInfo> kv2DictInfoMap(String kvString) {
        Map<String, DictInfo> _map = new HashMap<>();
        if (StringUtils.hasText(kvString)) {
            String[] sa = kvString.split("\n");
            Arrays.stream(sa).map(ss -> ss.split(":")).forEach(sb -> {
                if (sb.length >= 2) {
                    _map.put(sb[0].trim(), new DictInfo(sb[1].trim(), sb[0].trim(), sb.length > 2 && sb[2] != null ? sb[2].trim() : null));
                }
            });
        }
        return _map;
    }

    protected Ret<?> do_ajax(String uiType, String uid, String btnId, String[] id) {
        try {
            if (id == null) return Ret.fail(Ret.RetCode.invalid_param.getCode(), "未指定要操作的目标数据ID");
            FtlParser parser = createFtlParser();
            service.do_ajax(uiType, uid, btnId, id, parser);
            return Ret.success();
        } catch (Exception e) {
            return Ret.error(e);
        }
    }

    protected Rs rowPacket(Rs rs, ColumnMeta[] columns, FtlParser parser,
                           Map<String, Map<String, DictInfo>> kvMaps,
                           Map<String, Rs> _ktbmap, boolean isExport) {
        DictInfo _kv;//0:val 1:key 2:color
        String k, v;
        Rs ktbRs;
        Store store = null;
        for (ColumnMeta col : columns) {
            rs.set(col.getId(), rs.get(col.getField()));
            //如果未绑定字段（如模板显示输出），则取columnMeta的id作为字段名:
            String formattedName = isExport ? col.getId() : col.getId() + "__tplFmtTxt";//需要模板输出的，另用一个字段名，原字段值仍保留(但导出除外)
            if (col.getFormat() == ColFormatTpl.tpl) {
                try {
                    parser.addVariable("vo", rs);
                    String tps = parser.parse(col.getTpl());
                    rs.set(formattedName, isExport ? StringUtils.htmlToText(tps).trim() : tps);
                    parser.removeVariable("vo");
                } catch (Exception e) {
                    e.printStackTrace();
                    String s = "自定义模板输出格式有误，请检查列表界面配置."+e.getMessage();
                    rs.set(formattedName,isExport?s:"<span style=\"color:red\">"+s+"</span>");
                }
            } else if (col.getFormat() == ColFormatTpl.oneToMany) {
                rs.set(col.getId(), getManyString("select " + col.getJoinField() + " as ret from " + col.getJoinTable() +
                        " where " + col.getJoinRef() + "='" + rs.id() + "'"));
            } else if (col.getFormat() == ColFormatTpl.manyToMany) {
                TableInfo joinTableInfo = context.getDao().getTableInfo(col.getJoinTable());
                rs.set(col.getId(), getManyString("select t." + col.getJoinField() + " as ret from " + col.getJoinTable() +
                        " t inner join " + col.getManyTable() + " r on t." + joinTableInfo.getPrimaryKey() + "=r." + col.getManyField1() +
                        " where r." + col.getManyField0() + "='" + rs.id() + "'"));
            } else if ((col.getFormat() == ColFormatTpl.img || col.getFormat() == ColFormatTpl.file)) {
                if (store == null) store = context.getStore(rs.getTable(), rs.id());
                rs.put(col.getId() + "__store", store.get(col.getField()).getItems());
            } else if (rs.containsKey(col.getField()) && rs.get(col.getField()) != null) {
                v = rs.getString(col.getField());
                switch (col.getFormat()) {
                    case kv:
                        if (kvMaps.get(col.getId()) != null && (_kv = kvMaps.get(col.getId()).get(v)) != null) {
                            rs.set(col.getId(), _kv.getName());
                            if (!isExport && StringUtils.hasText(_kv.getColor())) {
                                rs.set(formattedName, GetDictInfo.withColor(_kv));
                            }
                        }
                        break;
                    case dict:
                        DictInfo di = context.getDict(col.getDictId(), v).orElse(null);
                        rs.set(col.getId(), di == null ? v : di.getName());
                        if (!isExport && di != null && StringUtils.hasText(di.getColor())) {
                            rs.set(formattedName, GetDictInfo.withColor(di));
                        }//for tree界面用(list界面直接在listRender中判断处理了)
                        break;
                    case manyToOne:
                        k = col.getJoinTable() + "@" + col.getField() + "@" + v;
                        if ((ktbRs = _ktbmap.get(k)) == null) {
                            ktbRs = context.getDao().rs(col.getJoinTable(), v);
                            _ktbmap.put(k, ktbRs);
                        }
                        rs.set(col.getId(), ktbRs.get(col.getJoinField()));
                        break;
                    case tree:
                        TreeUiMeta treeMeta = service.getUiMeta(col.getTreeId());
                        String selfId = Objects.equals(rs.getTable(), treeMeta.getMainTable()) ? rs.getString(rs.getPrimaryKey()) : null;
                        Rs treeNode = Objects.equals(rs.getTable(), treeMeta.getMainTable()) ? rs : context.getDao().rs(treeMeta.getMainTable(), rs.getString(col.getField()));
                        rs.set(formattedName, getTreePathString(col.getTreeDisplay(), treeMeta, treeNode, selfId, isExport));
                        break;
                    case datetime:
                        rs.set(col.getId(), DateUtils.fmt_datetime.format(rs.getLocalDateTime(col.getField())));
                        break;
                    case date:
                        rs.set(col.getId(), DateUtils.fmt_date.format(rs.getLocalDate(col.getField())));
                        break;
                    case time:
                        rs.set(col.getId(), new SimpleDateFormat("HH:mm:ss").format(rs.getDate(col.getField())));
                        break;
                    case number:
                        rs.set(col.getId(), new DecimalFormat("0.00").format(rs.getDecimal(col.getField())));
                        break;
                }
            }
        }
        return rs;
    }

    protected FtlParser createFtlParser() {
        return ftlInvoker.getParser();
    }

    protected void preUiMeta(UiMeta uiMeta, FtlParser parser) {
        if (StringUtils.hasText(uiMeta.getHeadTips())) {
            try {
                uiMeta.setHeadTips(parser.parse(uiMeta.getHeadTips()));
            } catch (Exception ex) {
            }
        }
        if (StringUtils.hasText(uiMeta.getFootTips())) {
            try {
                uiMeta.setFootTips(parser.parse(uiMeta.getFootTips()));
            } catch (Exception ex) {
            }
        }
    }

    protected String getTreePathString(TreePathTpl tpt, TreeUiMeta treeUiMeta, Rs rs, String selfId, boolean isExport) {
        String ret = rs.getString(treeUiMeta.getNameField()),
                pid = rs.getString(treeUiMeta.getPidField());
        Rs parent;
        boolean isSelf = selfId != null && selfId.equals(rs.getString(rs.getPrimaryKey()));
        String sp = tpt == TreePathTpl.arrow && isExport ? " > " : tpt.getValue();
        return TreePathTpl.leaf != tpt && StringUtils.hasText(pid) && !"0".equals(pid)
                && !(parent = context.getDao().rs(treeUiMeta.getMainTable(), pid)).isNew()
                ? getTreePathString(tpt, treeUiMeta, parent, selfId, isExport) + (isSelf ? "" : sp + ret)
                : (isSelf ? "" : ret);
    }

    protected String getManyString(String qry) {
        String ret = context.getDao().ls(qry, 0, 1)
                .getItems().stream().map(rs -> "；" + rs.getString("ret")).collect(Collectors.joining());
        return ret != null && ret.startsWith("；") ? ret.substring(1) : ret;
    }

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDateTime.class, new Java8MyDateEditor(Java8MyDateEditor.Type.LocalDateTime));
        binder.registerCustomEditor(LocalDate.class, new Java8MyDateEditor(Java8MyDateEditor.Type.LocalDate));
    }

    @Resource
    protected AdminContext context;
    @Resource
    private FtlInvoker ftlInvoker;
}
